tree grammar LPMEDLuaCodeGen;

options {
	tokenVocab=LPMEDLexer; // use the vocabulary from the parser
	ASTLabelType=CommonTree; // what kind of trees are we walking?
	output=template; // generate templates
	language = CSharp2;
}

@namespace { Core.ANTLRGrammars }

@header
{
    using CommonClasses.Utility;
    using Descriptions_DomainModel.Model;
    using System.Collections.Generic;
}

@members
{
	public string ModuleName;
    	public IList<string> WatchesNames;
	public DomainModel model;

      	string BoolToStr(bool v)
	{ 
		return (v)? "true" : "false";
	}

	private Node GetNodeByFullPath(string DevName, IList Nodes)
        {
            NodeGroup dev = model.NodeGroups.Find(obj => obj.Name == DevName);

            Node nod = dev.Nodes.Find(n => n.Name == (Nodes[0] as CommonTree).Text);

            for (int i = 1; i < Nodes.Count; i++ )
                nod = nod.Nodes.Find(n => n.Name == (Nodes[i] as CommonTree).Text);

            return nod;
        }

      	void FindVPinsByMethodName(
                                 string DDname,
                                 string DEVICEID,
    			         IList nodes,
                                 string sm,
                                 out int statePin, 
				 out int pout,
                                 out string signal)
      	{
		Node fnode = GetNodeByFullPath(DEVICEID, nodes);
       
       		// if we are finding THE Method, then we know it's name (sm)
	       	//if(sm!="*")
		//{
		        Method m=fnode.Methods.Find(tm => tm.Name == sm);
			statePin = fnode.StatePinNumber;
			pout = m.VirtualPinOut;
			//signal = BoolToStr(m.Signal);
			signal = m.Signal.ToString();
	        //}
		//else //we are finding all methods in node
		//{
		//        pouts=new List<int>(); pins=new List<int>(); 
		//        foreach(Method m in fnode.Methods)
		//	{
		//		pouts.Add(m.VirtualPinOut);
		//	        pins.Add(m.VirtualPinIn);
		//	}
		//	hold = true;
		//	signal = "true";
		//return true;
		//}
	}//FindVPinsByMethodName
	
	int GetNamedStateValue(string devName, IList nodes, string sname)
	{
		Node node = GetNodeByFullPath(devName, nodes);

		return node.NamedStates.Find(nameds => nameds.Name == sname).Value;
	}

	int GetStatePinNumber(string devName, IList nodes)
	{
		Node node = GetNodeByFullPath(devName, nodes);

		return node.StatePinNumber;
	}
}


program
scope
{
 string DDname;
}
:
	^(CPROGRAM ^(CDEVICE name=ID) {$program::DDname=$name.text; ModuleName = $name.text;} block)  -> program(name={$name.text}, codeBlock={$block.st})
;


blockStmt 	:	^(CSTATEMENT s+=stmt*) ->statemens(sts={$s});


stmt
options { backtrack=true;}
:			assign ->{$assign.st} 
		|	ifStmt ->{$ifStmt.st} 
		|	^(CWHILE expr blockStmt) ->while(expr={$expr.st},codeBlock={$blockStmt.st})
		|	^(CFOR a=assign expr b=assign blockStmt) ->for(initassign={$a.st},expr={$expr.st},assign={$b.st},codeBlock={$blockStmt.st})
		|	deviceFuncID ->{$deviceFuncID.st} 
		|	blockStmt ->{$blockStmt.st} 
		|	^(CBREAKPOINT INT) -> breakpoint(id = {$INT.Text})
		|	regMethodAccess -> {$regMethodAccess.st}
		|	CTERMINATEALL -> terminateall()
		|	functionCall -> {$functionCall.st} 
;

regMethodAccess
:
	^(CREGMETHACCESS ID expr) -> methodcall(text={$ID.Text}, value={$expr.st})
;

ifStmt
options { backtrack=true; }
: 
	^(CIFTE expr a=blockStmt b=blockStmt?)->if(expr={$expr.st},then={$a.st},else={(b != null)?$b.st : new  StringTemplate(" ")})
;


assign
:	
	^(CASSIGN ID comExpr) -> assign(left={$ID.Text},right={$comExpr.st})	
;


comExpr
:
	assignExpr ->{$assignExpr.st}
;


assignExpr
:	
			^(CASSIGNOP ID expr)->{$expr.st}
		|	expr -> {$expr.st}
;


expr options{backtrack=true;}
:	
			^(OpOr a=expr b=expr ) -> template(a={$a.st},b={$b.st})<< (<a>) or (<b>) >>
		|	^(OpAnd a=expr b=expr ) -> template(a={$a.st},b={$b.st})<< (<a>) and (<b>) >>
		|	^(OpLogicalB a=expr b=expr ) -> template(a={$a.st},op={$OpLogicalB.text},b={$b.st})<< (<a>) <op> (<b>) >>	
		|	^(OpLogicalA a=expr b=expr ) -> template(a={$a.st},op={$OpLogicalA.text},b={$b.st})<< (<a>) <op> (<b>) >>	
		|	^(OpAddSub a=expr b=expr ) -> template(a={$a.st},op={$OpAddSub.text},b={$b.st})<< (<a>) <op> (<b>) >>	
		|	^(OpDivMul a=expr b=expr ) -> template(a={$a.st},op={$OpDivMul.text},b={$b.st})<< (<a>) <op> (<b>) >>	
		|	^(OpNegation  a=expr ) -> template(a={$a.st})<< ~<a> >>	
		|	^(OpUnar a=expr ) -> template(a={$a.st})<< -<a> >>	
		|	'(' a=expr ')'  -> template(a={$a.st})<< (<a>) >>	
		|	atom ->{$atom.st}
;

atom options{backtrack=true;}	
scope
{
  int stateValue;
}
@init
{
  $atom::stateValue = 0;
}
:
			ID -> {%{$ID.Text}}
		|	^(CINT INT) -> {%{$INT.text}}
		|	^(CBOOL DBOOL) -> {%{$DBOOL.text}}
		|	^(CFLOAT FLOAT) -> {%{$FLOAT.text}}
		|	regFuncAccess -> {$regFuncAccess.st}	
		| ^(OBJECTNAME main = ID) ^(NODES nodes += ID*) ^(MEMBERNAME name = ID)
      { $atom::stateValue = GetNamedStateValue($main.Text, $nodes, $name.Text);}	
				-> {%{$atom::stateValue.ToString()}}	//named state
		|	^(CGETSTATE ^(OBJECTNAME devname=ID) ^(NODES nodes+=ID*))
				-> getPinStatus(statepin={GetStatePinNumber($devname.Text, $nodes).ToString()})
;

regFuncAccess	:	^(CREGFUNCACCESS ID) -> funccall(text={$ID.Text});



paramList 	:	^(CPARAM e+=expr+) ->paramList(list={$e});

deviceMemberID 	:	DEVICEID ids+=ID+ -> members(objectname={$DEVICEID.text},membersname={$ids});

deviceFuncID 	
scope{
//virtual pins of method (or list of pins of several methods)
 int pout; 
 int statePin;
 string signal;
}
:

//Call atomic method with parameters is not available now

/*	^(CCALL ^(CSYNCCALL ^(OBJECTNAME DEVICEID) ^(NODES ID*) ^(MEMBERNAME ID )) paramList)
		-> 	syncCall(vpout={$DEVICEID.Text},vpin={$paramList.st})*/

//atomic method can be called only asynchronous
/*|	^(CCALL ^(CSYNCCALL ^(OBJECTNAME DEVICEID) ^(NODES nodes+=ID*) ^(MEMBERNAME sm=ID)))		
		{
	                FindVPinsByMethodName(
				$program::DDname,
        		        $DEVICEID.Text,
				$nodes,
		                $sm.text,
		                out $deviceFuncID::statePin,
				out $deviceFuncID::pout, 
				out $deviceFuncID::signal);
		}
		-> syncCall(vpout={$deviceFuncID::pout},vpin={$deviceFuncID::}, signal={$deviceFuncID::signal})*/

//Call atomic method with parameters is not available now

/*|	^(CCALL ^(CASYNCCALL ^(OBJECTNAME DEVICEID) ^(NODES ID*) ^(MEMBERNAME ID)) paramList)
	        -> asyncCall(vpout={$DEVICEID.Text},signal={$paramList.st})*/

	^(CCALL ^(CASYNCCALL ^(OBJECTNAME devname=ID) ^(NODES nodes+=ID*) ^(MEMBERNAME sm=ID)))
		{
                        FindVPinsByMethodName(
        	                $program::DDname,
	                        $devname.Text,
				$nodes,
	                        $sm.text,
        	                out $deviceFuncID::statePin,
				out $deviceFuncID::pout,
				out $deviceFuncID::signal);
	        }
		-> asyncCall(vpout={$deviceFuncID::pout}, signal={$deviceFuncID::signal})

/*|	^(CCALL ^(CWAITTHECALL ^(OBJECTNAME DEVICEID) ^(NODES nodes+=ID*) ^(MEMBERNAME sm=ID)))
		{
                        FindVPinsByMethodName(
		                $program::DDname,
				$DEVICEID.Text,
				$nodes,
 	                        $sm.text,
         	                out $deviceFuncID::statePin,
				out $deviceFuncID::pout, 
				out $deviceFuncID::signal);
                }
		->	waitone(statepin={$deviceFuncID::statePin}, vpin={$deviceFuncID::pins[0]})*/
		
|	^(CCALL ^(CENDTHECALL ^(OBJECTNAME devname=ID) ^(NODES nodes+=ID*) ^(MEMBERNAME sm=ID)))
		{
                        FindVPinsByMethodName(
 	                       	$program::DDname,
	     	               	$devname.Text,
				$nodes,
	                        $sm.text,
         	                out $deviceFuncID::statePin,
				out $deviceFuncID::pout,
				out $deviceFuncID::signal);
                }
		->	endone(vpout={$deviceFuncID::pout})
		
/*|	^(CCALL ^(CWAITANYCALL ^(OBJECTNAME DEVICEID) ^(NODES ID*) ^(MEMBERNAME ID)) paramList)
		-> 	waitany(vpin_s={$DEVICEID.Text})*/
		
/*|	^(CCALL ^(CWAITANYCALL ^(OBJECTNAME DEVICEID) ^(NODES nodes+=ID*) ^(MEMBERNAME node=ID)))
		{
			$nodes.Add($node);
                        FindVPinsByMethodName(
                        	$program::DDname,
	                        $DEVICEID.Text,
				$nodes,
                		"*",
	                        out $deviceFuncID::pins,
				out $deviceFuncID::pouts, 
				out $deviceFuncID::hold, 
				out $deviceFuncID::signal);
                             
                }
		-> 	waitany(vpin_s={$deviceFuncID::pins})*/
;

functionCall
scope
{
	List<int> parameters;
}
: 
			^(CASYNCFUNCCALL ID parms+=parameter*) -> asyncFuncCall(name={$ID.text}, pars={$parms})
		| 	^(CWAITFUNCFINISH ID parms+=parameter*) -> waitFuncFinish(name={$ID.text})
		|	^(CSYNCFUNCCALL ID parms+=parameter*) -> syncFuncCall(name={$ID.text}, pars={$parms})
;


parameter	: 
	^(PARAM parameterValue) -> {$parameterValue.st}
;

parameterValue
scope
{
	int value;
}
@init
{
	$parameterValue::value = 0;
}
:
			ID -> {%{$ID.Text}}							//variable
		| 	INT ->	{%{$INT.Text}}							//number
		|	^(OBJECTNAME name=ID) ^(NODES nodes+=ID*) ^(MEMBERNAME sname=ID)
			{ $parameterValue::value = GetNamedStateValue($name.Text, $nodes, $sname.Text);}	
				-> {%{$parameterValue::value.ToString()}}	//named state
;

/////////////////////////////////////////////////////////////////////////////

//We don't mind about varialbes declarations, and we delete it from the tree grammar
//It's because Lua has dynamic type system.

varDecList : varDecListAtom+ ;

varDecListAtom :  ^(CTYPE TYPE peremDecList) ;
	
peremDecList : ID+;

block : (varDecList)* blockStmt -> {$blockStmt.st};

